
// 包声明：该工具类属于 com.xg.utils 包
package com.xg.validator.util;

// 导入自定义注解
import com.xg.validator.annotation.ConditionalRequired;
import com.xg.validator.annotation.MaxLength;
import com.xg.validator.annotation.Required;

// 导入自定义枚举和异常
import com.xg.model.enums.CompareOperator;
import com.xg.validator.exception.ValidationException;

// 导入标准验证相关类
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 验证工具类，用于统一处理对象的字段校验逻辑。
 *
 * 支持两种类型的校验：
 * 1. Spring Bean Validation（如 @Email）
 * 2. 自定义注解校验（如 @Required, @MaxLength, @ConditionalRequired）
 */
@Component
public class ValidationUtil {

    private final Validator validator;

    /**
     * 构造函数注入 Spring 提供的标准 Validator
     *
     * @param validator Spring 提供的 JSR-303 标准验证器
     */
    @Autowired
    public ValidationUtil(Validator validator) {
        this.validator = validator;
    }

    /**
     * 对传入的对象执行完整校验，并在失败时抛出异常
     *
     * @param obj 要校验的对象
     * @throws ValidationException 如果有校验错误发生
     */
    public void validateAndThrowIfFail(Object obj) throws ValidationException {
        Map<String, String> errors = new HashMap<>();

        // 1. 使用 Spring 的 Bean Validation 进行标准校验
        Set<ConstraintViolation<Object>> violations = validator.validate(obj);
        for (ConstraintViolation<Object> violation : violations) {
            String field = violation.getPropertyPath().toString();
            String message = violation.getMessage();
            errors.put(field, message);
        }

        // 2. 使用自定义注解进行扩展校验
        Map<String, String> customErrors = validate(obj);
        errors.putAll(customErrors);

        // 3. 如果存在任何错误，抛出异常
        if (!errors.isEmpty()) {
            throw new ValidationException(errors);
        }
    }

    /**
     * 自定义注解校验的核心实现
     *
     * 支持以下三种注解：
     * - @Required       : 字段必须非空
     * - @MaxLength      : 字符串长度不能超过指定值
     * - @ConditionalRequired : 条件必填字段
     *
     * @param obj 被校验的对象
     * @return 包含所有校验错误信息的 map，key 是字段名，value 是错误信息
     */
    private static Map<String, String> validate(Object obj) {
        Map<String, String> errors = new HashMap<>();

        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();

        // 先缓存所有字段的值，便于后续条件判断使用
        Map<String, Object> fieldValueMap = new HashMap<>();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                fieldValueMap.put(field.getName(), field.get(obj));
            } catch (IllegalAccessException e) {
                errors.put(field.getName(), "无法访问该字段");
            }
        }

        // 开始遍历每个字段并校验
        for (Field field : fields) {
            try {
                field.setAccessible(true);

                Object value = field.get(obj);

                // 必填校验 @Required
                if (field.isAnnotationPresent(Required.class)) {
                    if (value == null || (value instanceof String && ((String) value).trim().isEmpty())) {
                        Required required = field.getAnnotation(Required.class);
                        errors.put(field.getName(), required.message());
                    }
                }

                // 最大长度校验 @MaxLength
                if (field.isAnnotationPresent(MaxLength.class)) {
                    MaxLength maxLength = field.getAnnotation(MaxLength.class);
                    if (value instanceof String && ((String) value).length() > maxLength.value()) {
                        errors.put(field.getName(), "字段 " + field.getName() + " 超出最大长度：" + maxLength.value());
                    }
                }

                // 条件必填校验 @ConditionalRequired
                if (field.isAnnotationPresent(ConditionalRequired.class)) {
                    ConditionalRequired cond = field.getAnnotation(ConditionalRequired.class);
                    String conditionFieldName = cond.conditionField();   // 获取依赖字段名
                    String[] expectedValues = cond.conditionValues();   // 获取期望值数组
                    CompareOperator operator = cond.operator();         // 获取比较操作符

                    Object conditionValueObj = fieldValueMap.get(conditionFieldName); // 获取依赖字段值
                    Object currentValue = value;                        // 当前字段值

                    if (conditionValueObj == null) {
                        continue; // 如果依赖字段为空，则跳过当前字段的校验
                    }

                    boolean conditionMet = false;

                    // 处理字符串类型比较
                    if (conditionValueObj instanceof String && expectedValues != null && expectedValues.length > 0) {
                        String conditionValueStr = (String) conditionValueObj;
                        for (String expectedValue : expectedValues) {
                            if (conditionValueStr.equals(expectedValue)) {
                                conditionMet = true;
                                break;
                            }
                        }
                    }
                    // 处理整数类型比较
                    else if (conditionValueObj instanceof Integer && expectedValues != null && expectedValues.length > 0) {
                        int conditionValue = (Integer) conditionValueObj;
                        for (String expectedValueStr : expectedValues) {
                            try {
                                int expectedValue = Integer.parseInt(expectedValueStr);
                                switch (operator) {
                                    case EQ:
                                        if (conditionValue == expectedValue) {conditionMet = true;}
                                        break;
                                    case GT:
                                        if (conditionValue > expectedValue) {conditionMet = true;}
                                        break;
                                    case LT:
                                        if (conditionValue < expectedValue) {conditionMet = true;}
                                        break;
                                    case GTE:
                                        if (conditionValue >= expectedValue) {conditionMet = true;}
                                        break;
                                    case LTE:
                                        if (conditionValue <= expectedValue) {conditionMet = true;}
                                        break;
                                    default:
                                        throw new IllegalStateException("不支持的操作符类型: " + operator);
                                }
                                if (conditionMet) {break;}
                            } catch (NumberFormatException ignored) {
                                // 忽略非法数值
                            }
                        }
                    }

                    // 如果满足条件但当前字段为空，则记录错误
                    if (conditionMet) {
                        if (currentValue == null || (currentValue instanceof String && ((String) currentValue).trim().isEmpty())) {
                            errors.put(field.getName(), cond.message());
                        }
                    }
                }

            } catch (IllegalAccessException e) {
                errors.put(field.getName(), "无法访问该字段：" + e.getMessage());
            }
        }

        return errors;
    }
}